
#include <BStream>
#include <BXmlNode>
#include "member_BXmlNode.h"

using namespace BWE;

#define member					(*(member_BXmlNode*)_ptr)
#define member_allocate()		_ptr = new member_BXmlNode(this)
#define member_release()		delete (member_BXmlNode*)_ptr

BXmlNode::BXmlNode(const BString& name, BXmlNode* parent) : BObject(name)
{
	member_allocate();
	if (parent && parent != this)
	{
		setParent(parent);
	}
}
BXmlNode::~BXmlNode()
{
	member_release();
}

void BXmlNode::setFormat(const BString& format)
{
	if (member.format != format)
	{
		member.format = format;
		member.head_pre = 0;
		member.head_post = 0;
		member.head_in_pre = 0;
		member.head_in_post = 0;
		member.tail_pre = 0;
		member.tail_post = 0;
		member.tail_in_pre = 0;
		member.tail_in_post = 0;

		int tail_begin = format.find("</");
		if (tail_begin < format.size())
			member.tail_pre = format[tail_begin - 1];
		int tail_end = format.find("/>");
		if (tail_end < format.size() && tail_end + 2 < format.size())
			member.tail_post = format[tail_end + 2];

		int stop = bMin(tail_begin, tail_end);
		if (tail_begin < 0)
			stop = format.size();
		int head_begin = 0;
		int head_end = 0;
		while (head_begin < stop)
		{
			if (format[head_begin] == '<')
			{
				if (head_begin > 0)
					member.head_pre = format[head_begin - 1];
				while (head_end < stop)
				{
					if (format[head_end] == '>')
					{
						if (head_end < format.size() - 1)
							member.head_post = format[head_end + 1];
						if (head_end - head_begin > 1)
						{
							member.head_in_pre = format[head_begin + 1];
							member.head_in_post = format[head_end - 1];
						}
						break;
					}
					head_end++;
				}
				break;
			}
			head_begin++;
		}

		if (member.head_pre != '\n')
			member.head_pre = 0;
		if (member.head_post != '\n')
			member.head_post = 0;
		if (member.head_in_pre != '\n')
			member.head_in_pre = 0;
		if (member.head_in_post != '\n')
			member.head_in_post = 0;
		if (member.tail_pre != '\n')
			member.tail_pre = 0;
		if (member.tail_post != '\n')
			member.tail_post = 0;
		if (member.tail_in_pre != '\n')
			member.tail_in_pre = 0;
		if (member.tail_in_post != '\n')
			member.tail_in_post = 0;

	}
}
const BString& BXmlNode::format() const
{
	return member.format;
}

void BXmlNode::setText(const BString& text)
{
	if (member.text != text)
	{
		member.text = text;
		member.dirty = true;
	}
}
const BString& BXmlNode::text() const
{
	return member.text;
}

void BXmlNode::setAttrib(const BString& label, const BString& value)
{
	if (label.empty())
		return;
	member.attribs.push();
	AttribPair& attrib = member.attribs.last();
	attrib.name = label;
	attrib.value = value;
}
const BString& BXmlNode::attrib(const BString& name) const
{
	for (int i = 0; i < member.attribs.size(); i++)
	{
		const AttribPair& attrib = member.attribs[i];
		if (attrib.name == name)
			return attrib.value;
	}
	return member.attribs(-1).value;

}
bool BXmlNode::hasAttrib(const BString& label) const
{
	for (int i = 0; i < member.attribs.size(); i++)
	{
		const AttribPair& attrib = member.attribs[i];
		if (attrib.name == label)
			return true;
	}
	return false;
}

int BXmlNode::attribCount() const
{
	return member.attribs.size();
}
const BString& BXmlNode::attribName(int index) const
{
	return member.attribs(index).name;
}
const BString& BXmlNode::attribValue(int index) const
{
	return member.attribs(index).value;
}

void BXmlNode::clearAttribs()
{
	member.attribs.clear();
}

void BXmlNode::setParent(BXmlNode* parent)
{
	if (parent && parent != this)
	{
		parent->addChild(this);
	}
}
BXmlNode* BXmlNode::parent() const
{
	return member.parent;
}

void BXmlNode::addChild(BXmlNode* child)
{
	if (child && child != this && child != member.parent && !member.children.contain(child))
	{
		member.children.append(child);
		if (xmlnode_member(child)->parent)
			xmlnode_member(child)->parent->remove(child);
		xmlnode_member(child)->parent = this;
		member.dirty = true;
	}
}
BXmlNode* BXmlNode::addChild(const BString& name, const BString& text)
{
	BXmlNode* child = new BXmlNode(name);
	child->setText(text);
	member.children.append(child);
	xmlnode_member(child)->parent = this;
	member.dirty = true;
	return child;
}
BXmlNode* BXmlNode::addText(const BString& text)
{
	BXmlNode* textNode = new BXmlNode();
	textNode->setText(text);

	member.children.append(textNode);
	xmlnode_member(textNode)->parent = this;
	member.dirty = true;
	return textNode;
}

const BString& BXmlNode::plainText() const
{
	if (member.dirty)
	{
		member.plainText.reset();
		member.plainText.append(member.text);
		for (int i = 0; i < member.children.size(); i++)
		{
			const BString& text = member.children[i]->plainText();
			member.plainText.append(text);
		}
		member.dirty = false;
	}
	return member.plainText;
}

void BXmlNode::remove(BXmlNode* child)
{
	if (child && member.children.contain(child))
	{
		xmlnode_member(child)->parent = 0;
		member.children.remove(child);
		member.dirty = true;
	}
}
void BXmlNode::remove(const BString& desc)
{
	if (desc.contains(':') || desc.contains('&') || desc.contains('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
			{
				xmlnode_member(child)->parent = 0;
				member.children.remove(i--);
				member.dirty = true;
			}
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
			{
				xmlnode_member(child)->parent = 0;
				member.children.remove(i--);
				member.dirty = true;
			}
		}
	}
}
void BXmlNode::clear()
{
	for (auto it = member.children.begin(); it != member.children.end(); it++)
	{
		BXmlNode* child = *it;
		xmlnode_member(child)->parent = 0;
		member.dirty = true;
	}
	member.plainText.clear();
	member.dirty = false;
}

int BXmlNode::childCount() const
{
	return member.children.size();
}
BXmlNode* BXmlNode::child(int index)
{
	return member.children(index);
}
const BXmlNode* BXmlNode::child(int index) const
{
	return member.children(index);
}
BXmlNode* BXmlNode::child(const BString& desc)
{
	if (desc.contains(':') || desc.contains('&') || desc.contains('|'))
	{
		XmlFilter filter(desc);
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (filter(child))
				return child;
		}
	}
	else
	{
		for (int i = 0; i < member.children.size(); i++)
		{
			BXmlNode* child = member.children[i];
			if (child->name() == desc)
				return child;
		}
	}
	return 0;
}

BXmlNode* BXmlNode::find(const BString& name)
{
	if (this->name() == name)
		return this;
	for (int i = 0; i < member.children.size(); i++)
	{
		BXmlNode* child = member.children[i];
		BXmlNode* node = child->find(name);
		if (node)
			return node;
	}
	return 0;
}
const BXmlNode* BXmlNode::find(const BString& name) const
{
	if (this->name() == name)
		return this;
	for (int i = 0; i < member.children.size(); i++)
	{
		const BXmlNode* child = member.children[i];
		const BXmlNode* node = child->find(name);
		if (node)
			return node;
	}
	return 0;
}

BXmlNode* BXmlNode::prev()
{
	if (member.parent)
	{
		auto it = xmlnode_member(member.parent)->children.seek(this);
		it--;
		if (it.valid())
			return *it;
	}
	return 0;
}
BXmlNode* BXmlNode::next()
{
	if (member.parent)
	{
		auto it = xmlnode_member(member.parent)->children.seek(this);
		it++;
		if (it.valid())
			return *it;
	}
	return 0;
}

BXmlNode* BXmlNode::locate(const BString& desc)
{
	BStringList namelst = desc.split('/');
	BXmlNode* node = this->child(namelst.first());
	namelst.remove(0);
	while (node && namelst.size())
	{
		const BString& text = namelst.first();
		node = node->child(text);
		namelst.remove(0);
	}
	return node;
}
